import WebGl from '@/modules/three';
import {
  SUBTRACTION,
  INTERSECTION,
  ADDITION,
  Brush,
  Evaluator,
} from 'three-bvh-csg';

const { Three, TWEEN, dat, Stats, OrbitControls } = WebGl;

class WebGlControl {
  private container: any;
  private camera: any;
  private scene: any;
  private renderer: any;
  private stats: any;
  private animateFrame: any;
  private tween: any;
  private controls: any;
  private clock: any;

  private evaluator: any;
  private baseBrush: any;
  private brush: any;
  private resultBrush: any;
  private wireframe: any;
  private core: any;

  private Geometrys: {
    starsGeometry?: any;
  } = {}; //保存所有Geometry，方便统一销毁
  private Materials: {
    starsMaterial?: any;
  } = {}; //保存所有Material，方便统一销毁
  private particleSystem: any;

  private GuiInit: any = null;
  private guiData: any = {
    x: 1,
    color: '#000000',
    autoRotate: true,

    operation: SUBTRACTION, //布尔模式
    useGroups: true, //组合
    wireframe: false, //网格

    brushScale: 1,
    brushTranslate: 0,
  };

  public init = (id: string) => {
    // if(this.scene) {return}
    this.container = document.getElementById(id);
    let width = this.container.clientWidth,
      height = this.container.clientHeight;

    this.clock = new Three.Clock();

    this.scene = new Three.Scene();

    this.camera = new Three.PerspectiveCamera(60, width / height, 1, 2000);
    this.camera.position.x = 0;
    this.camera.position.y = 0;
    this.camera.position.z = 500;
    this.camera.lookAt(this.scene.position);

    this.renderer = new Three.WebGLRenderer();
    // this.renderer.setClearColor( 0xffffff );
    this.renderer.setPixelRatio(window.devicePixelRatio);
    this.renderer.setSize(width, height);
    this.container.appendChild(this.renderer.domElement);

    this.controls = new OrbitControls(this.camera, this.renderer.domElement);
    this.controls.enableDamping = true; //阻尼
    this.controls.dampingFactor = 0.2; //阻尼

    this.addThings();
    this.addLight(); //+光源
    this.animate(); //开始动
    this.initStats();

    this.initGui();
  };
  public destroy = () => {
    this.toggleGui(0);
    Object.values(this.Geometrys).forEach((e) => {
      e.dispose();
    });
    Object.values(this.Materials).forEach((e) => {
      e.dispose();
    });
    this.scene.clear();
    this.renderer.dispose();
    this.renderer.forceContextLoss();
    this.camera = null;
    this.scene = null;
    this.renderer = null;
    this.GuiInit = null;
    this.stats = null;
    if (this.tween) {
      this.tween.stop();
      this.tween = null;
    }
    this.controls = null;
    this.evaluator = null;
    this.baseBrush = null;
    this.brush = null;
    this.core = null;
    cancelAnimationFrame(this.animateFrame);
  };

  private initStats = () => {
    this.stats = new Stats();
    this.stats.showPanel(0); // 0: fps, 1: ms, 2: mb, 3+: custom
    this.stats.dom.style.position = 'absolute';
    this.container.appendChild(this.stats.dom);
  };

  public initGui = () => {
    if (this.GuiInit) {
      this.toggleGui();
      return;
    }
    this.GuiInit = new dat.GUI({
      // autoPlace: false
    });

    this.GuiInit.domElement.style.position = 'absolute';
    this.GuiInit.domElement.style.right = '10px';
    this.GuiInit.domElement.style.top = '10px';

    let GuiController = this.GuiInit.addFolder('控制窗口');
    GuiController.add(this.guiData, 'x', 0, 2, 0.1).name('自转速度');
    GuiController.addColor(this.guiData, 'color').name('背景颜色');
    GuiController.open();

    let GuiController2 = this.GuiInit.addFolder('布尔控制');
    GuiController2.add(this.guiData, 'operation', {
      SUBTRACTION,
      INTERSECTION,
      ADDITION,
    });
    GuiController2.add(this.guiData, 'wireframe');
    GuiController2.add(this.guiData, 'useGroups');
    GuiController2.add(this.guiData, 'brushScale', 0.5, 2, 0.1).name(
      '圆柱缩放',
    );
    GuiController2.add(this.guiData, 'brushTranslate', -100, 100, 1).name(
      '圆柱位移',
    );

    GuiController2.open();
    // let cubeGui = this.GuiInit.addFolder("cube position");
    // cubeGui.open()
  };

  public toggleGui = (state: any = null) => {
    if (!this.GuiInit || !this.GuiInit.domElement) {
      return;
    }
    let display = this.GuiInit.domElement.style.display == 'none' ? '' : 'none';
    if (state == 0) {
      display = 'none';
    }
    this.GuiInit.domElement.style.display = display;
  };

  private addLight = () => {
    let ambi = new Three.AmbientLight(0x686868);
    this.scene.add(ambi);
    let spotLight = new Three.DirectionalLight(0xffffff);
    spotLight.position.set(550, 100, 550);
    spotLight.intensity = 0.6;
    this.scene.add(spotLight);
    // let light = new Three.PointLight( 0xFFFF00 );
    // light.position.set( -100, 100, 0 );
    // this.scene.add(light);
  };

  private addThings = () => {
    // const axesHelper = new Three.AxesHelper( 800 );
    // this.scene.add( axesHelper )  //添加坐标轴；红线是X轴，绿线是Y轴，蓝线是Z轴

    this.evaluator = new Evaluator();

    this.baseBrush = new Brush(
      new Three.IcosahedronGeometry(120, 10),
      new Three.MeshStandardMaterial({
        flatShading: true,

        polygonOffset: true,
        polygonOffsetUnits: 1,
        polygonOffsetFactor: 1,
      }),
    );

    this.brush = new Brush(
      new Three.CylinderGeometry(100, 100, 350, 45),
      new Three.MeshStandardMaterial({
        color: 0x80cbc4,

        polygonOffset: true,
        polygonOffsetUnits: 1,
        polygonOffsetFactor: 1,
      }),
    );

    this.core = new Brush(
      new Three.IcosahedronGeometry(40, 1),
      new Three.MeshStandardMaterial({
        flatShading: true,
        color: 0xff9800,
        emissive: 0xff9800,
        emissiveIntensity: 0.35,

        polygonOffset: true,
        polygonOffsetUnits: 1,
        polygonOffsetFactor: 1,
      }),
    );
    this.core.castShadow = true;
    this.scene.add(this.core);

    // create wireframe
    this.wireframe = new Three.Mesh(
      undefined,
      new Three.MeshBasicMaterial({ color: 0x009688, wireframe: true }),
    );
    this.scene.add(this.wireframe);

    this.initTween();
  };

  private initTween = () => {
    // var pos: any = { val: 1 };
    // this.tween = new TWEEN.Tween(pos)
    //   .to({ val: 0 }, 1000)
    //   .easing(TWEEN.Easing.Quadratic.InOut)
    //   .delay(200)
    //   .onUpdate(callback);
    // let tweenBack = new TWEEN.Tween(pos)
    //   .to({ val: 1 }, 1000)
    //   .easing(TWEEN.Easing.Quadratic.InOut)
    //   .delay(200)
    //   .onUpdate(callback);
    // this.tween.chain(tweenBack);
    // tweenBack.chain(this.tween);
    // this.tween.start();
    // let that = this;
    // function callback(res) {
    //   that.Materials.starsMaterial.uniforms.val.value = res.val;
    // }
  };

  private updateCSG = () => {
    this.evaluator.useGroups = this.guiData.useGroups;
    this.resultBrush = this.evaluator.evaluate(
      this.baseBrush,
      this.brush,
      Number(this.guiData.operation),
      this.resultBrush,
    );

    this.resultBrush.castShadow = true;
    this.resultBrush.receiveShadow = true;
    this.scene.add(this.resultBrush);
  };

  private animate = () => {
    // if(!this.renderer){return;}
    if (this.stats) {
      this.stats.begin();
    }

    this.render();
    TWEEN.update();
    this.controls.update();
    if (this.stats) {
      this.stats.end();
    }
    this.animateFrame = requestAnimationFrame(this.animate);
  };
  private render = () => {
    const delta = this.clock.getDelta();

    if (this.renderer) {
      // update the csg
      this.updateCSG();
      if (this.wireframe && this.resultBrush) {
        this.wireframe.geometry = this.resultBrush.geometry;
        this.wireframe.visible = this.guiData.wireframe;
      }
      this.brush.rotation.x += delta * this.guiData.x;
      this.brush.rotation.z += delta * this.guiData.x * 1.2;
      this.brush.scale.set(this.guiData.brushScale, 1, this.guiData.brushScale);
      this.brush.position.set(0, this.guiData.brushTranslate, 0);
      this.brush.updateMatrixWorld();

      this.renderer.setClearColor(this.guiData.color, 1.0);
      // if(this.guiData.autoRotate){
      // }
      this.renderer.render(this.scene, this.camera);
    }
  };
}

const webGlControl = new WebGlControl();
export default webGlControl;
